초기 설정¶

In [118]:
from plotly.offline import plot, iplot, init_notebook_mode
init_notebook_mode(connected=True)
In [117]:
%%HTML
<script src="./require.js"></script>

상관관계 분석¶

유닛 코스트의 합, 유닛 개수의 합, 유닛 단계의 평균과 순위와의 상관관계 분석¶

In [91]:
# 선택한 열들에 대한 상관 분석 수행
correlation_matrix = revised_match_unit[
    ["cost_sum", "avg_tier", "unit_count", "placement"]
].corr()

상관관계 히트맵¶

In [92]:
fig = plt.figure(figsize=(5, 5))
fig.suptitle("유닛 코스트의 합, 유닛 개수, 유닛 단계의 평균과 순위와의 상관관계", x=0.5, size=12)
ax = fig.add_subplot(1, 1, 1)
sns.heatmap(
    correlation_matrix,
    annot=True,
    ax=ax,
    cmap=sns.color_palette("Blues", as_cmap=True),
    linewidths=0.01,
)

plt.tight_layout()
plt.show()

각 항목의 히스토그램과 선 그래프¶

In [93]:
sns.set_theme(style='white', font=font_family, color_codes='deep')

fig = plt.figure(figsize=(10, 10))
# cost_sum과 placement
ax1_1 = fig.add_subplot(3, 1, 1)
ax1_2 = ax1_1.twinx()
data = revised_match_unit.sort_values(["cost_sum", "placement"])
sns.histplot(
    revised_match_unit,
    x="cost_sum",
    hue="placement",
    multiple="stack",
    palette=sns.color_palette("Blues_r", as_cmap=True),
    edgecolor="0.6",
    linewidth=0.4,
    log_scale=False,
    bins=40,
    ax=ax1_1,
)
sns.lineplot(data=data, x="cost_sum", y="placement", color='#FA8258', ax=ax1_2)
ax1_1.set_title("cost_sum과 placement")
ax1_2.set_ylim(data["placement"].max() + 0.1, data["placement"].min() - 0.1)

# unit_count와 placement
ax2_1 = fig.add_subplot(3, 1, 2)
ax2_2 = ax2_1.twinx()
data = revised_match_unit.sort_values(["unit_count", "placement"])
sns.histplot(
    revised_match_unit,
    x="unit_count",
    hue="placement",
    multiple="stack",
    palette=sns.color_palette("Blues_r", as_cmap=True),
    edgecolor="0.6",
    linewidth=0.4,
    log_scale=False,
    bins=8,
    ax=ax2_1,
)

sns.lineplot(data=data, x="unit_count", y="placement", color='#FA8258', ax=ax2_2)
ax2_1.set_title("unit_count와 placement")
ax2_2.set_ylim(data["placement"].max() + 0.1, data["placement"].min() - 0.1)

# avg_tier와 placement
ax3_1 = fig.add_subplot(3, 1, 3)
ax3_2 = ax3_1.twinx()
data = revised_match_unit.sort_values(["avg_tier", "placement"])
sns.histplot(
    revised_match_unit,
    x="avg_tier",
    hue="placement",
    multiple="stack",
    palette=sns.color_palette("Blues_r", as_cmap=True),
    edgecolor="0.6",
    linewidth=0.4,
    log_scale=False,
    bins=8,
    ax=ax3_1,
)

sns.lineplot(data=data, x="avg_tier", y="placement", color='#FA8258', ax=ax3_2)
ax3_1.set_title("avg_tier와 placement")
ax3_2.set_ylim(data["placement"].max() + 0.1, data["placement"].min() - 0.1)

plt.tight_layout()
plt.show()

통계 분석¶

각 유닛 조합의 등장 비율과 평균순위¶

In [94]:
# 가장 최신 버전의 match_player_id 리스트를 추출
latest_match_player = match.merge(preprocessed_match_player, how='inner', left_on='match_id', right_on='match_id')
latest_match_player = latest_match_player[
    (latest_match_player["version_major"] == VERSION_MAJOR)
    & (latest_match_player["version_minor"] == VERSION_MINOR)
    & (latest_match_player["version_patch"] == VERSION_PATCH)
]
latest_match_player_id = latest_match_player['match_player_id']

# 최신 버전에 사용된 유닛만 추출
latest_match_unit = revised_match_unit.loc[revised_match_unit['match_player_id'].isin(latest_match_player_id)]
# 슬라이싱
latest_match_unit = latest_match_unit.loc[:, ['name', 'placement']]
In [95]:
# df의 list_str 컬럼의 모든 조합에 대하여 numeric_str 컬럼의
# frequency와 frequency percentage, average, combination length를 계산하여 result_df를 반환합니다.
def calculate_combination_statistic(df, list_str, numeric_str):
    # 각 조합의 평균 순위와 빈도수를 저장할 defaultdict 초기화
    combo_data = defaultdict(lambda: {numeric_str: [], "count": 0})
    
    # 데이터프레임 순회하며 조합의 평균 순위와 빈도수 계산
    for index, row in df.iterrows():
        items = row[list_str]
        numeric = row[numeric_str]
    
        # 플레이어가 사용한 조합 추가
        combo = tuple(sorted(items))
        combo_data[combo][numeric_str].append(numeric)
        combo_data[combo]["count"] += 1
    
        # 조합의 부분집합들 추가
        for r in range(1, len(items)):
            for subset in combinations(items, r):
                subset_combo = tuple(sorted(subset))
                combo_data[subset_combo][numeric_str].append(numeric)
                combo_data[subset_combo]["count"] += 1
    
    # 각 조합 및 부분집합들의 평균 순위와 빈도수를 데이터프레임으로 변환
    result_data = []
    for combo, data in combo_data.items():
        avg_rank = sum(data[numeric_str]) / data["count"]
        percentage = data["count"] / len(df)
        result_data.append(
            {"combination": combo, "avg_rank": avg_rank, "frequency": data["count"],
             "percentage": percentage, "length" : len(combo)}
        )
    
    result_df = pd.DataFrame(result_data)
    return result_df
In [96]:
# 모든 조합의 통계를 계산
unit_combination_stat = calculate_combination_statistic(latest_match_unit, 'name', 'placement')
# 조합을 유닛의 비용 기준으로 정렬
# 유닛의 비용 dict를 생성
unit_cost = preprocessed_match_unit.groupby(by='name').first().reset_index().loc[:,['name', 'single_cost']].\
                    sort_values(['single_cost', 'name'])
unit_cost = {row['name']:row['single_cost'] for i, row in unit_cost.iterrows()}

# 튜플의 내용을 유닛의 코스트로 정렬한 후 str으로 변환
sorted_combs = unit_combination_stat['combination'].apply(lambda champs: ", ".join(sorted(champs, key=lambda champ: unit_cost[champ])))
unit_combination_stat.loc[:,['combination']] = sorted_combs
In [97]:
# 비율이 기준 이하인 조합은 제외
criteria = 0.001
limited_unit_comb_stat = unit_combination_stat.loc[unit_combination_stat['percentage'] > criteria]
dot5_combination_stat = unit_combination_stat.loc[unit_combination_stat['percentage'] > 0.005]
per1_combination_stat = unit_combination_stat.loc[unit_combination_stat['percentage'] > 0.01]
In [98]:
# 점 그래프 생성
fig = px.scatter(
    dot5_combination_stat,
    x="percentage",
    y="avg_rank",
    size="percentage",
    color='length',
    hover_name="combination",
    title="각 조합의 등장 비율과 평균 순위",
    labels={
        "percentage": "비율",
        "avg_rank": "평균 순위",
        "length": "길이"
    },
    log_x=False,
    color_continuous_scale=px.colors.sequential.Jet,
    custom_data=["frequency", "percentage", "length"],
)

# 툴팁 추가
fig.update_traces(
    hovertemplate="<br>".join(
        [
            "<b>조합</b>: %{hovertext}<br>",
            "<b>평균 순위</b>: %{y:.4f}",
            "<b>빈도</b>: %{customdata[0]}",
            "<b>비율</b>: %{customdata[1]:.3%}",
            "<b>길이</b>: %{customdata[2]}"
        ]
    )
)
# y축 반전
fig.update_yaxes(autorange="reversed")

# 그래프 출력 사이즈 조절
fig.update_layout(
    width=1100,  # 너비
    height=800,  # 높이
    hovermode='closest'  # 가장 가까운 데이터 포인트의 툴팁 표시
)
# 그래프 표시
fig.show()

전체 조합에 대한 평균순위 히스토그램¶

In [99]:
sns.set_theme(style='darkgrid', font=font_family, color_codes='deep')

fig = plt.figure(figsize=(10,10))
fig.suptitle(f"{criteria*100}% 이상 등장한 조합")
ax1 = fig.add_subplot(2,2,1)
ax2 = fig.add_subplot(2,2,2)
ax3 = fig.add_subplot(2,2,3)
ax4 = fig.add_subplot(2,2,4)

# 그래프 1
data = limited_unit_comb_stat

ax = sns.histplot(
    data,
    x="avg_rank",
    hue="length",
    multiple="stack",
    palette=sns.color_palette("Blues_r", as_cmap=True),
    edgecolor="0.6",
    linewidth=0.4,
    log_scale=False,
    kde=True,
    ax=ax1
)

ax.set_title("전체 조합에 대한 평균순위 히스토그램(스택)")
ax.set_xlim(data["avg_rank"].max() * 1.05, data["avg_rank"].min() * 0.9)

# 그래프 2
data = limited_unit_comb_stat.loc[(limited_unit_comb_stat['length']==1)]

ax = sns.histplot(
    data,
    x="avg_rank",
    edgecolor="0.6",
    linewidth=0.4,
    log_scale=False,
    kde=True,
    ax=ax2
)

ax.set_title("단일 유닛 조합에 대한 평균순위 히스토그램")
ax.set_xlim(data["avg_rank"].max() * 1.05, data["avg_rank"].min() * 0.95)

# 그래프 3
data = limited_unit_comb_stat.loc[(limited_unit_comb_stat['length']<=6) & (limited_unit_comb_stat['length']>1)]

ax = sns.histplot(
    data,
    x="avg_rank",
    hue="length",
    # multiple="stack",
    palette=sns.color_palette("Blues", as_cmap=True),
    edgecolor="0.6",
    linewidth=0.4,
    log_scale=False,
    kde=True,
    ax=ax3
)

ax.set_title("길이가 2~6인 조합에 대한 평균순위 히스토그램")
ax.set_xlim(data["avg_rank"].max() * 1.05, data["avg_rank"].min() * 0.9)

# 그래프 4
data = limited_unit_comb_stat.loc[(limited_unit_comb_stat['length']>6)]

sns.set_theme(style='darkgrid', font=font_family, color_codes='deep')

ax = sns.histplot(
    data,
    x="avg_rank",
    hue="length",
    # multiple="stack",
    palette=sns.color_palette("Blues", as_cmap=True),
    edgecolor="0.6",
    linewidth=0.4,
    log_scale=False,
    kde=True,
    ax=ax4
)

ax.set_title("길이가 7 이상인 조합에 대한 평균순위 히스토그램")
ax.set_xlim(data["avg_rank"].max() * 1.05, data["avg_rank"].min() * 0.8)

plt.tight_layout()
plt.show()
  • 조합한 데이터의 평균 순위 분포를 확인하고 게임 밸런스를 파악하기 위해서 히스토그램을 그렸습니다.
  • 전체 조합에 대한 평균순위 히스토그램에서는 가장 많이 등장한 평균 순위는 1등에서 8등의 평균인 4.5등이 나올거라 생각했지만 그렇지 않았습니다.
  • 이유는 아까 살펴봤듯이, 등수가 높아질수록 평균적으로 유닛의 개수가 증가합니다. 따라서 조합의 길이가 증가할수록 상위 등수를 차지한 플레이어의 조합의 빈도가 증가하기 때문에 각 길이별 조합의 가장 많이 등장한 평균 순위는 우측으로 이동합니다.
  • 그래서 길이가 2 이상부터는 게임 밸런스를 파악하는데 크게 신뢰할 수 없을 것이라 생각되고 길이가 1인 단일 유닛의 분포를 확인하면 게임밸런스를 어느정도 파악 가능할 것으로 봅니다.

  • 순위의 평균인 4.5 근처의 유닛들은 밸런스가 잘 잡힌 유닛이라 파악할 수 있습니다.

  • 순위 평균 4.5 이하의 유닛들은 선택 시 게임의 순위가 낮아질 가능성이 높은 유닛들로써 추후 버프 대상으로 고려할 수 있습니다.
  • 반대로, 순위 평균 4.5 이상의 유닛들은 선택 시 게임의 순위가 높아질 가능성이 높은 유닛들로써 추후 너프 대상으로 고려할 수 있습니다.
  • 전체적으로 4.5위 근처에 많은 유닛들이 배치되어 게임 밸런스적으로는 괜찮다고 생각됩니다.

단일 유닛의 사용 빈도, 사용 비율, 평균 순위 히트맵¶

In [100]:
single_unit_stat = per1_combination_stat[per1_combination_stat['length']==1]
single_unit_stat = single_unit_stat.loc[:, ['combination', 'avg_rank', 'frequency', 'percentage']]
single_unit_stat = single_unit_stat.set_index('combination')
single_unit_stat.sort_values(by='avg_rank', inplace=True)
single_unit_avg_rank = single_unit_stat.loc[:,['avg_rank']]

dropped_single_unit_stat = single_unit_stat.drop('avg_rank', inplace=False, axis=1)
dropped_single_unit_stat = dropped_single_unit_stat.sort_values(by='frequency', ascending=False)
In [101]:
scaler = StandardScaler()
scaled_data = scaler.fit_transform(dropped_single_unit_stat)
scaled_df = pd.DataFrame(scaled_data, columns=dropped_single_unit_stat.columns, index=dropped_single_unit_stat.index)

fig = plt.figure(figsize=(8, 12))

# GridSpec 설정
gs = gridspec.GridSpec(1, 2, width_ratios=[2, 1])  # 1행 2열, 첫 번째 그래프 폭을 두 번째 그래프 폭의 2배로 지정

ax1 = fig.add_subplot(gs[0])
ax2 = fig.add_subplot(gs[1])

heatmap = sns.heatmap(scaled_df, annot=False, cmap="RdBu_r", fmt=".2f", ax=ax1)

# Adjust the alpha value for the colormap
heatmap.get_children()[0].set_alpha(0.7)  # Adjust the alpha value (0.0 to 1.0)

# 주석 추가로 원래의 값을 나타냅니다.
for i in range(len(dropped_single_unit_stat.index)):
    for j in range(len(dropped_single_unit_stat.columns)):
        if j==1:
            ax1.text(j + 0.5, i + 0.5, f"{dropped_single_unit_stat.iloc[i, j]*100:.2f}%", ha="center", va="center", color="Black", fontsize=9)
            continue
        ax1.text(j + 0.5, i + 0.5, f"{dropped_single_unit_stat.iloc[i, j]:.2f}", ha="center", va="center", color="Black", fontsize=9)
            
ax1.set_title("단일 유닛의 사용 빈도, 사용 비율")
ax1.set_ylabel("유닛 이름")
ax1.yaxis.set_tick_params(labelsize=9)

# graph2
scaled_data = scaler.fit_transform(single_unit_avg_rank)
scaled_df = pd.DataFrame(scaled_data, columns=single_unit_avg_rank.columns, index=single_unit_avg_rank.index)

# 순위 데이터의 기준과 다른 데이터의 기준을 맞추기
scaled_df['avg_rank'] =scaled_df['avg_rank'].apply(lambda x: -x)

heatmap = sns.heatmap(scaled_df, annot=False, cmap="RdBu_r", fmt=".2f", ax=ax2)

# Adjust the alpha value for the colormap
heatmap.get_children()[0].set_alpha(0.7)  # Adjust the alpha value (0.0 to 1.0)

# 주석 추가로 원래의 값을 나타냅니다.
for i in range(len(single_unit_avg_rank.index)):
    for j in range(len(single_unit_avg_rank.columns)):
        ax2.text(j + 0.5, i + 0.5, f"{single_unit_avg_rank.iloc[i, j]:.2f}", ha="center", va="center", color="Black", fontsize=9)
            
ax2.set_title("단일 유닛의 평균순위")
ax2.set_ylabel("유닛 이름")
ax2.yaxis.set_tick_params(labelsize=9)

plt.tight_layout()
# plt.savefig("graph.png")  # 파일 경로와 이름 지정
plt.show()

단일 유닛의 사용 비율, 평균 순위 히트맵(Top10, Bottom10)¶

In [102]:
def plot_avgrank_percentage_heatmap(rank_df, percent_df, fig_size=(6,6), item_size=10):
    # 위에서 10개와 아래에서 10개의 데이터 선택
    selected_data = pd.concat([rank_df.head(item_size), rank_df.tail(item_size)])
    
    fig = plt.figure(figsize=fig_size)
    
    ax1 = fig.add_subplot(1,2,1)
    ax2 = fig.add_subplot(1,2,2)
    
    # graph1
    heatmap = sns.heatmap(selected_data,  annot=True, cmap="RdBu", fmt=".2f", ax=ax1, annot_kws={"size": 9})
    
    # Adjust the alpha value for the colormap
    heatmap.get_children()[0].set_alpha(0.7)  # Adjust the alpha value (0.0 to 1.0)
                
    ax1.yaxis.set_tick_params(labelsize=9)
    
    # graph2
    selected_data = pd.concat([percent_df.head(item_size), percent_df.tail(item_size)])
    heatmap = sns.heatmap(selected_data, annot=True, cmap="RdBu_r", fmt=".2%", ax=ax2, annot_kws={"size": 9})
    
    # Adjust the alpha value for the colormap
    heatmap.get_children()[0].set_alpha(0.7)  # Adjust the alpha value (0.0 to 1.0)
                
    ax2.yaxis.set_tick_params(labelsize=9)
    
    plt.tight_layout(pad=2)
    # plt.savefig("graph.png")  # 파일 경로와 이름 지정
    plt.show()
In [103]:
def get_sorted_column_df(df, index, columns, ascending=True):
    result_df = df.loc[:, [index, *columns]]
    result_df = result_df.set_index(index)
    result_df.sort_values(by=columns, inplace=True, ascending=ascending)
    return result_df
In [104]:
unit_stat_len1 = per1_combination_stat[per1_combination_stat['length']==1]

unit_stat_len1_avgrank = get_sorted_column_df(unit_stat_len1, 'combination', ['avg_rank'])
unit_stat_len1_percent = get_sorted_column_df(unit_stat_len1, 'combination', ['percentage'], False)
plot_avgrank_percentage_heatmap(unit_stat_len1_avgrank, unit_stat_len1_percent, fig_size=(6,6))

길이 2 이상인 유닛 조합의 사용 비율, 평균 순위 히트맵(Top10, Bottom10)¶

In [105]:
def plot_avgrank_percentage_heatmap_col2(rank_df, percent_df, fig_size=(6,6), title="", shape=(1,2), item_size=10, font_size=9, label_size=9):
    selected_data = pd.concat([rank_df.head(item_size), rank_df.tail(item_size)])
    
    scaler = StandardScaler()
    scaled_data = scaler.fit_transform(selected_data)
    scaled_df = pd.DataFrame(scaled_data, columns=selected_data.columns, index=selected_data.index)
    
    # 순위 데이터의 기준과 다른 데이터의 기준을 맞추기
    scaled_df['avg_rank'] =scaled_df['avg_rank'].apply(lambda x: -x)
    fig = plt.figure(figsize=fig_size)
    fig.suptitle(title)
    
    ax1 = fig.add_subplot(*shape,1)
    ax2 = fig.add_subplot(*shape,2)
    
    heatmap = sns.heatmap(scaled_df, annot=False, cmap="RdBu_r", fmt=".2f", ax=ax1)
    
    # Adjust the alpha value for the colormap
    heatmap.get_children()[0].set_alpha(0.7)  # Adjust the alpha value (0.0 to 1.0)
    
    # 주석 추가로 원래의 값을 나타냅니다.
    for i in range(len(selected_data.index)):
        for j in range(len(selected_data.columns)):
            if j==1:
                ax1.text(j + 0.5, i + 0.5, f"{selected_data.iloc[i, j]*100:.2f}%", ha="center", va="center", color="Black", fontsize=font_size)
                continue
            ax1.text(j + 0.5, i + 0.5, f"{selected_data.iloc[i, j]:.2f}", ha="center", va="center", color="Black", fontsize=font_size)
                
    ax1.yaxis.set_tick_params(labelsize=label_size)
    
    # graph2
    selected_data = pd.concat([percent_df.head(item_size), percent_df.tail(item_size)])
    
    scaled_data = scaler.fit_transform(selected_data)
    scaled_df = pd.DataFrame(scaled_data, columns=selected_data.columns, index=selected_data.index)
    
    # 순위 데이터의 기준과 다른 데이터의 기준을 맞추기
    scaled_df['avg_rank'] =scaled_df['avg_rank'].apply(lambda x: -x)
    
    heatmap = sns.heatmap(scaled_df, annot=False, cmap="RdBu_r", fmt=".2f", ax=ax2)
    
    # Adjust the alpha value for the colormap
    heatmap.get_children()[0].set_alpha(0.7)  # Adjust the alpha value (0.0 to 1.0)
    
    # 주석 추가로 원래의 값을 나타냅니다.
    for i in range(len(selected_data.index)):
        for j in range(len(selected_data.columns)):
            if j==0:
                ax2.text(j + 0.5, i + 0.5, f"{selected_data.iloc[i, j]*100:.2f}%", ha="center", va="center", color="Black", fontsize=font_size)
                continue
            ax2.text(j + 0.5, i + 0.5, f"{selected_data.iloc[i, j]:.2f}", ha="center", va="center", color="Black", fontsize=font_size)
                
    ax2.yaxis.set_tick_params(labelsize=label_size)
    
    plt.tight_layout()
    # plt.savefig("graph.png")  # 파일 경로와 이름 지정
    plt.show()
In [106]:
sns.set_theme(style='darkgrid', font=font_family)
unit_stat_len2 = per1_combination_stat[per1_combination_stat['length']==2]

unit_stat_len2_avgrank = get_sorted_column_df(unit_stat_len2, 'combination', ['avg_rank', 'percentage'])
unit_stat_len2_percent = get_sorted_column_df(unit_stat_len2, 'combination', ['percentage', 'avg_rank'], False)
plot_avgrank_percentage_heatmap_col2(unit_stat_len2_avgrank, unit_stat_len2_percent, fig_size=(8,6), title="길이가 2인 조합")

unit_stat_len7 = per1_combination_stat[per1_combination_stat['length']==7]

unit_stat_len7_avgrank = get_sorted_column_df(unit_stat_len7, 'combination', ['avg_rank', 'percentage'])
unit_stat_len7_percent = get_sorted_column_df(unit_stat_len7, 'combination', ['percentage', 'avg_rank'], False)
plot_avgrank_percentage_heatmap_col2(unit_stat_len7_avgrank, unit_stat_len7_percent, fig_size=(8,10), shape=(2,1), title="길이가 7인 조합")

unit_stat_len8 = dot5_combination_stat[dot5_combination_stat['length']==8]

unit_stat_len8_avgrank = get_sorted_column_df(unit_stat_len8, 'combination', ['avg_rank', 'percentage'])
unit_stat_len8_percent = get_sorted_column_df(unit_stat_len8, 'combination', ['percentage', 'avg_rank'], False)
plot_avgrank_percentage_heatmap_col2(unit_stat_len8_avgrank, unit_stat_len8_percent, fig_size=(8,10), shape=(2,1), title="길이가 8인 조합")

각 특성 조합의 등장 비율과 평균순위¶

In [107]:
latest_match_trait['placement'] = latest_match_trait['match_player_id'].apply(lambda x: int(x.split('_')[2]))
In [109]:
# 모든 조합의 통계를 계산
trait_combination_stat = calculate_combination_statistic(latest_match_trait, 'name', 'placement')
In [110]:
# 튜플의 내용을 유닛의 코스트로 정렬한 후 str으로 변환
sorted_combs = trait_combination_stat['combination'].apply(lambda traits: ", ".join(sorted(traits)))
trait_combination_stat.loc[:,['combination']] = sorted_combs
# 비율이 기준 이하인 조합은 제외
criteria = 0.005
limited_trait_comb_stat = trait_combination_stat.loc[trait_combination_stat['percentage'] > criteria]
dot5_trait_comb_stat = trait_combination_stat.loc[trait_combination_stat['percentage'] > 0.005]
per1_trait_comb_stat = trait_combination_stat.loc[trait_combination_stat['percentage'] > 0.01]
In [111]:
# 점 그래프 생성
fig = px.scatter(
    limited_trait_comb_stat,
    x="percentage",
    y="avg_rank",
    size="percentage",
    color='length',
    hover_name="combination",
    title="각 조합의 등장 비율과 평균 순위",
    labels={
        "percentage": "비율",
        "avg_rank": "평균 순위",
        "length": "길이"
    },
    log_x=False,
    color_continuous_scale=px.colors.sequential.Jet,
    custom_data=["frequency", "percentage", "length"],
)

# 툴팁 추가
fig.update_traces(
    hovertemplate="<br>".join(
        [
            "<b>조합</b>: %{hovertext}<br>",
            "<b>평균 순위</b>: %{y:.4f}",
            "<b>빈도</b>: %{customdata[0]}",
            "<b>비율</b>: %{customdata[1]:.3%}",
            "<b>길이</b>: %{customdata[2]}"
        ]
    )
)
# y축 반전
fig.update_yaxes(autorange="reversed")

# 그래프 출력 사이즈 조절
fig.update_layout(
    width=1100,  # 너비
    height=800,  # 높이
    hovermode='closest'  # 가장 가까운 데이터 포인트의 툴팁 표시
)
# 그래프 표시
fig.show()

전체 조합에 대한 평균순위 히스토그램¶

In [112]:
sns.set_theme(style='darkgrid', font=font_family, color_codes='deep')

fig = plt.figure(figsize=(10,10))
fig.suptitle(f"{criteria*100}% 이상 등장한 조합")
ax1 = fig.add_subplot(2,2,1)
ax2 = fig.add_subplot(2,2,2)
ax3 = fig.add_subplot(2,2,3)
ax4 = fig.add_subplot(2,2,4)

# 그래프 1
data = limited_trait_comb_stat

ax = sns.histplot(
    data,
    x="avg_rank",
    hue="length",
    multiple="stack",
    palette=sns.color_palette("Blues_r", as_cmap=True),
    edgecolor="0.6",
    linewidth=0.4,
    log_scale=False,
    kde=True,
    ax=ax1
)

ax.set_title("전체 조합에 대한 평균순위 히스토그램(스택)")
ax.set_xlim(data["avg_rank"].max() * 1.05, data["avg_rank"].min() * 0.9)

# 그래프 2
data = limited_trait_comb_stat.loc[(limited_trait_comb_stat['length']<=1)]

ax = sns.histplot(
    data,
    x="avg_rank",
    edgecolor="0.6",
    linewidth=0.4,
    log_scale=False,
    kde=True,
    ax=ax2
)

ax.set_title("단일 특성에 대한 평균순위 히스토그램")
ax.set_xlim(data["avg_rank"].max() * 1.05, data["avg_rank"].min() * 0.95)

# 그래프 3
data = limited_trait_comb_stat.loc[(limited_trait_comb_stat['length']<=4) & (limited_trait_comb_stat['length']>1)]

ax = sns.histplot(
    data,
    x="avg_rank",
    hue="length",
    # multiple="stack",
    palette=sns.color_palette("Blues", as_cmap=True),
    edgecolor="0.6",
    linewidth=0.4,
    log_scale=False,
    kde=True,
    ax=ax3
)

ax.set_title("길이가 2~4인 특성 조합에 대한 평균순위 히스토그램")
ax.set_xlim(data["avg_rank"].max() * 1.05, data["avg_rank"].min() * 0.9)

# 그래프 4
data = limited_trait_comb_stat.loc[(limited_trait_comb_stat['length']>4)]

sns.set_theme(style='darkgrid', font=font_family, color_codes='deep')

ax = sns.histplot(
    data,
    x="avg_rank",
    hue="length",
    # multiple="stack",
    palette=sns.color_palette("Blues", as_cmap=True),
    edgecolor="0.6",
    linewidth=0.4,
    log_scale=False,
    kde=True,
    ax=ax4
)

ax.set_title("길이가 5 이상인 특성 조합에 대한 평균순위 히스토그램")
ax.set_xlim(data["avg_rank"].max() * 1.05, data["avg_rank"].min() * 0.8)

plt.tight_layout()
plt.show()

단일 특성의 사용 빈도, 사용 비율, 평균 순위 히트맵¶

In [113]:
single_trait_stat = per1_trait_comb_stat[per1_trait_comb_stat['length']==1]
single_trait_stat = single_trait_stat.loc[:, ['combination', 'avg_rank', 'frequency', 'percentage']]
single_trait_stat = single_trait_stat.set_index('combination')
single_trait_stat.sort_values(by='avg_rank', inplace=True)
single_trait_avg_rank = single_trait_stat.loc[:,['avg_rank']]

dropped_single_trait_stat = single_trait_stat.drop('avg_rank', inplace=False, axis=1)
dropped_single_trait_stat = dropped_single_trait_stat.sort_values(by='frequency', ascending=False)
In [114]:
scaler = StandardScaler()
scaled_data = scaler.fit_transform(dropped_single_trait_stat)
scaled_df = pd.DataFrame(scaled_data, columns=dropped_single_trait_stat.columns, index=dropped_single_trait_stat.index)

fig = plt.figure(figsize=(8, 6))

# GridSpec 설정
gs = gridspec.GridSpec(1, 2, width_ratios=[2, 1])  # 1행 2열, 첫 번째 그래프 폭을 두 번째 그래프 폭의 2배로 지정

ax1 = fig.add_subplot(gs[0])
ax2 = fig.add_subplot(gs[1])

heatmap = sns.heatmap(scaled_df, annot=False, cmap="RdBu_r", fmt=".2f", ax=ax1)

# Adjust the alpha value for the colormap
heatmap.get_children()[0].set_alpha(0.7)  # Adjust the alpha value (0.0 to 1.0)

# 주석 추가로 원래의 값을 나타냅니다.
for i in range(len(dropped_single_trait_stat.index)):
    for j in range(len(dropped_single_trait_stat.columns)):
        if j==1:
            ax1.text(j + 0.5, i + 0.5, f"{dropped_single_trait_stat.iloc[i, j]*100:.2f}%", ha="center", va="center", color="Black", fontsize=9)
            continue
        ax1.text(j + 0.5, i + 0.5, f"{dropped_single_trait_stat.iloc[i, j]:.2f}", ha="center", va="center", color="Black", fontsize=9)
            
ax1.set_title("단일 특성의 사용 빈도, 사용 비율")
ax1.set_ylabel("특성 이름")
ax1.yaxis.set_tick_params(labelsize=9)

# graph2
scaled_data = scaler.fit_transform(single_trait_avg_rank)
scaled_df = pd.DataFrame(scaled_data, columns=single_trait_avg_rank.columns, index=single_trait_avg_rank.index)

# 순위 데이터의 기준과 다른 데이터의 기준을 맞추기
scaled_df['avg_rank'] =scaled_df['avg_rank'].apply(lambda x: -x)

heatmap = sns.heatmap(scaled_df, annot=False, cmap="RdBu_r", fmt=".2f", ax=ax2)

# Adjust the alpha value for the colormap
heatmap.get_children()[0].set_alpha(0.7)  # Adjust the alpha value (0.0 to 1.0)

# 주석 추가로 원래의 값을 나타냅니다.
for i in range(len(single_trait_avg_rank.index)):
    for j in range(len(single_trait_avg_rank.columns)):
        ax2.text(j + 0.5, i + 0.5, f"{single_trait_avg_rank.iloc[i, j]:.2f}", ha="center", va="center", color="Black", fontsize=9)
            
ax2.set_title("단일 특성의 평균순위")
ax2.set_ylabel("특성 이름")
ax2.yaxis.set_tick_params(labelsize=9)

plt.tight_layout()
# plt.savefig("graph.png")  # 파일 경로와 이름 지정
plt.show()

길이 2 이상인 유닛 조합의 사용 비율, 평균 순위 히트맵(Top10, Bottom10)¶

In [115]:
sns.set_theme(style='darkgrid', font=font_family)
trait_stat_len2 = per1_trait_comb_stat[per1_trait_comb_stat['length']==3]

trait_stat_len2_avgrank = get_sorted_column_df(trait_stat_len2, 'combination', ['avg_rank', 'percentage'])
trait_stat_len2_percent = get_sorted_column_df(trait_stat_len2, 'combination', ['percentage', 'avg_rank'], False)
plot_avgrank_percentage_heatmap_col2(trait_stat_len2_avgrank, trait_stat_len2_percent, fig_size=(9,6), title="길이가 2인 조합")